package map.core.util;

import com.alibaba.fastjson2.JSONObject;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.geotools.api.coverage.grid.GridEnvelope;
import org.geotools.api.parameter.GeneralParameterValue;
import org.geotools.api.parameter.ParameterValue;
import org.geotools.api.parameter.ParameterValueGroup;
import org.geotools.api.referencing.crs.CoordinateReferenceSystem;
import org.geotools.api.referencing.crs.EngineeringCRS;
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.coverage.grid.io.AbstractGridCoverageWriter;
import org.geotools.coverage.grid.io.AbstractGridFormat;
import org.geotools.coverage.grid.io.imageio.GeoToolsWriteParams;
import org.geotools.gce.geotiff.GeoTiffFormat;
import org.geotools.gce.geotiff.GeoTiffWriteParams;
import org.geotools.gce.geotiff.GeoTiffWriter;
import org.geotools.image.ImageWorker;

import java.awt.image.RenderedImage;
import java.awt.image.WritableRaster;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
import java.util.Map;

public class RasterFileHelper {

    private static final GeoTiffFormat TIFF_FORMAT = new GeoTiffFormat();
    private static final float DEFAULT_QUALITY = 0.75f;


    private static GeoTiffWriteParams buildWriteParams(GridCoverage2D coverage) {
        final RenderedImage renderedImage = coverage.getRenderedImage();
        int tileWidth = renderedImage.getTileWidth();
        int tileHeight = renderedImage.getTileHeight();

        // avoid tiles bigger than the image
        final GridEnvelope gr = coverage.getGridGeometry().getGridRange();
        if (gr.getSpan(0) < tileWidth) {
            tileWidth = gr.getSpan(0);
        }
        if (gr.getSpan(1) < tileHeight) {
            tileHeight = gr.getSpan(1);
        }

        GeoTiffWriteParams writeParams = new GeoTiffWriteParams();
        writeParams.setTilingMode(GeoToolsWriteParams.MODE_EXPLICIT);
        writeParams.setTiling(tileWidth, tileHeight);
        return writeParams;
    }

    private static ParameterValueGroup buildGeoToolsWriteParams(GeoTiffWriteParams writeParams) {
        final ParameterValueGroup wparams = TIFF_FORMAT.getWriteParameters();
        wparams.parameter(AbstractGridFormat.GEOTOOLS_WRITE_PARAMS.getName().toString())
                .setValue(writeParams);
        return wparams;
    }

    private static float extractQuality(Map<String, Object> encodingParameters) {
        float quality = DEFAULT_QUALITY;
        if (encodingParameters != null
                && !encodingParameters.isEmpty()
                && encodingParameters.containsKey("quality")) {
            String compressionQuality = (String) encodingParameters.get("quality");
            try {
                quality = Float.parseFloat(compressionQuality);
            } catch (NumberFormatException nfe) {
                throw new RuntimeException(nfe);
            }
        }
        return quality;
    }


     public static void saveCoverage2(GridCoverage2D coverage2D, File geotiff) throws IOException {

        if (!geotiff.getParentFile().exists()) {
            geotiff.getParentFile().mkdirs();
        }


        System.out.println("rs env=========>");
        System.out.println(coverage2D.getEnvelope());
        final ParameterValue<Boolean> tfw = GeoTiffFormat.WRITE_TFW.createValue();
        tfw.setValue(true);

        final ParameterValue<List> elevation = GeoTiffFormat.ELEVATION.createValue();

        GeoTiffWriter writer = new GeoTiffWriter(geotiff);
        //writer.write(coverage2D, new GeneralParameterValue[]{tfw});
        writer.write(coverage2D, new GeneralParameterValue[]{tfw});
        writer.dispose();

    }


     public static void saveCoverage(GridCoverage2D coverage2D,File file)   {
        try{

            file.getParentFile().mkdirs();
            System.out.println("write to->" + file);
            OutputStream out = new FileOutputStream(file);
            RasterFileHelper.write(out, coverage2D);

            out.flush();
            out.close();
        }
        catch (Exception e){
            throw new RuntimeException(e);
        }

    }

    public static void write(OutputStream stream, GridCoverage2D coverage) throws IOException {


        // for optimal extraction performance
        GeoTiffWriteParams imageIoWriteParams = buildWriteParams(coverage);
        ParameterValueGroup geotoolsWriteParams = buildGeoToolsWriteParams(imageIoWriteParams);
        CoordinateReferenceSystem crs = coverage.getCoordinateReferenceSystem();
        boolean unreferenced = crs == null || crs instanceof EngineeringCRS;


        if (unreferenced) {
            RenderedImage ri = coverage.getRenderedImage();
            int tileWidth, tileHeight;
            if (imageIoWriteParams.getTilingMode() == GeoToolsWriteParams.MODE_EXPLICIT) {
                tileWidth = imageIoWriteParams.getTileWidth();
                tileHeight = imageIoWriteParams.getTileHeight();
            } else {
                tileWidth = ri.getTileWidth();
                tileHeight = ri.getTileHeight();
            }
            float quality = DEFAULT_QUALITY;
            String compression = null;

            if (imageIoWriteParams.getCompressionMode() == GeoToolsWriteParams.MODE_EXPLICIT) {
                compression = imageIoWriteParams.getCompressionType();
                quality = imageIoWriteParams.getCompressionQuality();
            }


            new ImageWorker(ri).writeTIFF(stream, compression, quality, tileWidth, tileHeight);
        } else {
            final GeneralParameterValue[] wps =
                    geotoolsWriteParams
                            .values()
                            .toArray(
                                    new GeneralParameterValue
                                            [geotoolsWriteParams.values().size()]);

            // write out the coverage
            AbstractGridCoverageWriter writer =
                    (AbstractGridCoverageWriter) TIFF_FORMAT.getWriter(stream);
            if (writer == null)
                throw new RuntimeException(
                        "Could not find the GeoTIFF writer, please check it's in the classpath");
            try {
                writer.write(coverage, wps);
            } finally {
                try {
                    writer.dispose();
                } catch (Exception e) {
                    // swallow, silent close
                }
            }
        }
    }

    public static File getMetaFile(File tifFile) {
        return new File(tifFile.getParent(), FilenameUtils.getBaseName(tifFile.getName()) + ".meta");
    }


     public static void saveMeta(File file, double minValue, double maxValue) throws IOException {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("minValue", minValue);
        jsonObject.put("maxValue", maxValue);
        FileUtils.write(getMetaFile(file), jsonObject.toString());
    }




    public static double[] calMinAndMax(GridCoverage2D grid) {
        WritableRaster raster = grid.getRenderedImage().copyData(null);

        double minValue = Double.MAX_VALUE;
        double maxValue = Double.MIN_VALUE;

        int height = raster.getHeight();
        int width = raster.getWidth();


        float[] pixelValues = new float[1];
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                pixelValues = raster.getPixel(x, y, pixelValues);
                float value = pixelValues[0];
                if (value > maxValue) {
                    maxValue = value;
                }
                if (value < minValue) {
                    minValue = value;
                }

            }
        }

        return new double[]{minValue, maxValue};

    }


}
